﻿using CustomCharacter;
using Exiled.API.Features;
using PlayerRoles;
using RgCore.GameData;
using RgCore.PlayerHud;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using CustomCharacter.Interfaces;
using static InventoryStuff.Items;
using RgCore.Menu;
using RgCore.RoleAssignment;
using RgCore.Modules;
using RgCore.GameModes;
using RgCore.Database;

namespace InventoryStuff
{
    public interface IUsable
    {
        bool OnUse(Player player);
    }

    public interface IPurchasable
    {
        int Cost { get; }
    }

    public interface ISellable
    {
        int Sell_Cost { get; }
    }

    public class InventoryPlugin : RgPlugin
    {
        public override string Name => "物品系统";

        public override string Id => "inventory_plugin";

        public override Exiled.API.Enums.PluginPriority Priority => Exiled.API.Enums.PluginPriority.Lowest;

        public override void OnDisabled()
        {
            if (DatabaseController.DatabaseDisabled)
            {
                return;
            }
            InventoryHandler.SetEnabled(false);
            base.OnDisabled();
        }

        public override void OnEnabled()
        {
            if (DatabaseController.DatabaseDisabled)
            {
                Log.Warn("由于数据库未启用，此插件不加载");
                return;
            }
            InventoryHandler.SetEnabled(true);
            base.OnEnabled();
        }
    }

    public abstract class Item
    {
        protected Item(string id, string name, string description, bool forceshow = false)
        {
            Id = id;
            Name = name;
            Description = description;
            ShowRegardlessOfPossession = forceshow;
        }

        public string Id { get; }
        public string Name { get; }
        public string Description { get; }
        public bool ShowRegardlessOfPossession { get; set; }
    }

    public static class InventoryHandler
    {
        public static List<Item> RegisteredItem = new List<Item>();
        public static Dictionary<string, Dictionary<Item, int>> AllStuffs = new Dictionary<string, Dictionary<Item, int>>();
        public static Page RootPage = null;

        public static string UseItem(Player player, Item item)
        {
            if (!(item is IUsable use))
            {
                return "该物品不可在此使用，是否有其他使用途径?";
            }
            var stuff = AllStuffs[player.UserId].FirstOrDefault(x => x.Key == item);
            if (stuff.Key == null || stuff.Value == 0)
            {
                return "你没有该物品";
            }
            if (use.OnUse(player))
            {
                AllStuffs[player.UserId][item]--;
                GameplayData_OnUploadingData(player);
            }
            return "";
        }

        public static string SellItem(Player player, Item item, bool all)
        {
            if (!(item is ISellable sell))
            {
                return "该物品不可出售";
            }
            var stuff = AllStuffs[player.UserId].FirstOrDefault(x => x.Key == item);
            if (stuff.Key == null || stuff.Value == 0)
            {
                return "你没有该物品";
            }
            var data = GameplayData.GameDatas.FirstOrDefault(x => x.Player == player);
            if (data == null)
                return "数据异常";
            data.AddPoints(sell.Sell_Cost * (all ? stuff.Value : 1), true);
            AllStuffs[player.UserId][stuff.Key] -= all ? stuff.Value : 1;
            GameplayData_OnUploadingData(player);
            return "出售成功！";
        }

        public static string BuyItem(Player player, Item item)
        {
            if (!(item is IPurchasable buy))
            {
                return "该物品为非卖品";
            }
            var data = GameplayData.GameDatas.FirstOrDefault(x => x.Player == player);
            if (data == null)
                return "数据异常";
            if (data.Points < buy.Cost)
            {
                return "你的积分不足以购买该物品";
            }
            if (!AllStuffs[player.UserId].ContainsKey(item))
            {
                AllStuffs[player.UserId].Add(item, 0);
            }
            AllStuffs[player.UserId][item]++;
            data.AddPoints(-buy.Cost, true);
            GameplayData_OnUploadingData(player);
            return "购买成功！";
        }

        public static MenuElement[] ItemMenu(Player player, Item item)
        {
            var page = MenuInterface.MenuInterfaces.FirstOrDefault(x => x.Player == player).CurrentPage;
            var stuff = AllStuffs[player.UserId].FirstOrDefault(x => x.Key == item);
            if (stuff.Key == null)
                stuff = new KeyValuePair<Item, int>(item, 0);
            List<MenuElement> list = new List<MenuElement>() {
                new Text(page, "Description", () => $"<color=#00FF00>描述</color>: {stuff.Key.Description}", false),
                new Text(page, "Remain", () => $"<color=#00FF00>剩余数量</color>: {stuff.Value}", false),
            };
            if (stuff.Key is IUsable use)
                list.Add(new Option(page, "Use", "使用", null, null, (p) => UseItem(p, stuff.Key), false));
            if (stuff.Key is IPurchasable buy)
                list.Add(new Option(page, "Buy", $"购买(<color=#FFD700>-{buy.Cost}</color>)", null, null, (p) => BuyItem(p, stuff.Key), false));
            if (stuff.Key is ISellable sell)
            {
                list.Add(new Option(page, "Sell", $"出售(<color=#FFD700>+{sell.Sell_Cost}</color>)", null, null, (p) => SellItem(p, stuff.Key, false), false));
                if (stuff.Value > 1)
                    list.Add(new Option(page, "SellAll", $"全部出售(<color=#FFD700>+{sell.Sell_Cost * stuff.Value}</color>)", null, null, (p) => SellItem(p, stuff.Key, true), false));
            }
            return list.ToArray();
        }

        public static MenuElement[] InventoryMenu(Player player)
        {
            List<MenuElement> elements = new List<MenuElement>();
            var stuffs = AllStuffs[player.UserId];
            foreach (var item in RegisteredItem)
            {
                string color = "#0596ff";
                if (item is ObstacleItem)
                    color = "#ff5900";
                if (item is UnknownItem)
                    color = "#cb5581";
                if ((stuffs.ContainsKey(item) && stuffs[item] > 0) || item is IPurchasable || item.ShowRegardlessOfPossession)
                {
                    string count = (!stuffs.ContainsKey(item) || stuffs[item] == 0) ? "<color=#FF0000>x0</color>" : $"<color=#00FF00>x{stuffs[item]}</color>";
                    elements.Add(new Page(RootPage, item.Id, item.Name,
                        (_) => $"[<color={color}>{item.Name}</color> {count}]", null, (p) => ItemMenu(p, item), false));
                }
            }
            foreach (var unk in AllStuffs[player.UserId].Where(x => x.Key is UnknownItem))
            {
                elements.Add(new Page(RootPage, unk.Key.Id, unk.Key.Name,
                        (_) => $"[<color=#cb5581>{unk.Key.Name}</color> <color=#00FF00>x{unk.Value}</color>]", null, (p) => ItemMenu(p, unk.Key), false));
            }
            return elements.ToArray();
        }

        public static void SetEnabled(bool b)
        {
            if (b)
            {
                RegisterAllItems();
                RootPage = new Page(MenuInterface.Root, "Inventory", "个人库存", null, null, InventoryMenu, false);
                MenuInterface.RegisteredElements.Add(RootPage);
                GameplayData.OnInitializingData += GameplayData_OnInitializingData;
                GameplayData.OnUploadingData += GameplayData_OnUploadingData;
            }
            else
            {
                MenuInterface.RegisteredElements.Remove(RootPage);
                RootPage = null;
                GameplayData.OnInitializingData -= GameplayData_OnInitializingData;
                GameplayData.OnUploadingData -= GameplayData_OnUploadingData;
            }
        }

        public static async void GameplayData_OnInitializingData(Player p)
        {
            if (DatabaseController.DatabaseDisabled)
                return;
            Dictionary<Item, int> items = new Dictionary<Item, int>();
            var filter = new Dictionary<string, object> { { "userid", p.UserId } };
            QueryCommandRequest q = new QueryCommandRequest(RgCore.RgCore.instance.Config.Database.PlayerTable, filter, false, "inventory");
            var result = await q.GetResponseResult();
            var key = result.FirstOrDefault();
            if (key == null)
                return;
            try
            {
                string[] stuffs = ((string)key["inventory"]).Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (string stuff in stuffs)
                {
                    string[] array = stuff.Split('|');
                    string id = array[0];
                    int count = int.Parse(array[1]);
                    var item = RegisteredItem.FirstOrDefault(x => x.Id == id) ?? new UnknownItem(id);
                    items.Add(item, count);
                }
                if (!AllStuffs.ContainsKey(p.UserId))
                    AllStuffs.Add(p.UserId, items);
                AllStuffs[p.UserId] = items;
            }
            catch (Exception e)
            {
                p.SendFormattedMsg("error", "<color=#AA0000>库存错误</color>", $"请反馈技术人员 代码：{e.Message}", 10, MsgType.Failed_Response, ScreenLocation.Top, false);
            }
        }

        public static async void GameplayData_OnUploadingData(Player p)
        {
            if (DatabaseController.DatabaseDisabled) 
                return;
            if (!AllStuffs.ContainsKey(p.UserId))
            {
                return;
            }
            var filter = new Dictionary<string, object> { { "userid", p.UserId } };
            var items = AllStuffs[p.UserId];
            string text = "";
            foreach (var item in items.Where(x => x.Value > 0))
            {
                text += $"{item.Key.Id}|{item.Value};";
            }
            NoResponseCommandRequest cmd = new NoResponseCommandRequest(RgCore.RgCore.instance.Config.Database.PlayerTable,
                RequestType.Update, new Dictionary<string, object>() { { "inventory", text } }, filter);
            await cmd.ProcessNonQuery();
        }

        public static void GiveStuff(this Player player, string itemid, int count = 1)
        {
            var type = RegisteredItem.FirstOrDefault(x => x.Id == itemid);
            if (type == null)
                return;
            var key = AllStuffs[player.UserId];
            if (!key.ContainsKey(type))
            {
                key.Add(type, 0);
            }
            key[type] += count;
            GameplayData_OnUploadingData(player);
        }

        internal static void RegisterAllItems()
        {
            RegisteredItem.Clear();
            string desc = "使用后，开局将在<b>条件允许</b>下分配此角色";
            RegisteredItem.Add(new BroadcastCard(20));
            RegisteredItem.Add(new ScpTrialCard("Scp173Trial", "SCP-173 体验卡", desc, 350, RoleTypeId.Scp173));
            RegisteredItem.Add(new ScpTrialCard("Scp096Trial", "SCP-096 体验卡", desc, 450, RoleTypeId.Scp096));
            RegisteredItem.Add(new ScpTrialCard("Scp049Trial", "SCP-049 体验卡", desc, 300, RoleTypeId.Scp049));
            RegisteredItem.Add(new ScpTrialCard("Scp106Trial", "SCP-106 体验卡", desc, 300, RoleTypeId.Scp106));
            RegisteredItem.Add(new ScpTrialCard("Scp939Trial", "SCP-939 体验卡", desc, 300, RoleTypeId.Scp939));
            RegisteredItem.Add(new ScpTrialCard("Scp079Trial", "SCP-079 体验卡", desc, 350, RoleTypeId.Scp079));
            RegisteredItem.Add(new ScpTrialCard("Scp3114Trial", "SCP-3114 体验卡", desc, 300, RoleTypeId.Scp3114));
            RegisteredItem.Add(new ScpTrialCard("RandomScpTrial", "随机官方SCP 体验卡", desc, 250, RoleTypeId.None));
            if (ModuleManager.IsLoaded("CustomCharacter"))
            {
                RegisterCustomRoleTrialCard();
            }
            //Obstacle
            RegisteredItem.Add(new ObstacleItem("Scp2498Trial", "SCP-2498 体验卡", 350));
            RegisteredItem.Add(new ObstacleItem("MarauderTrial", "掠夺者 体验卡", 350));
            RegisteredItem.Add(new ObstacleItem("RandomCharacterTrial", "随机插件角色 体验卡", 200));
        }

        private static void RegisterCustomRoleTrialCard()
        {
            foreach (var key in CustomCharacterPlugin.instance.Manager.AllTypes.Keys)
            {
                var inst = (CustomCharacter.CustomCharacter)Activator.CreateInstance(key, 0);
                if (inst is ITrialCard itc)
                {
                    var attr = key.GetCustomAttribute<RoleInfoAttribute>();
                    var x = new CustomRoleTrialCard($"{key.Name}Trial", $"{attr.Name} 体验卡", "使用后，开局将在<b>条件允许</b>下分配此角色", itc.Cost, key);
                    RegisteredItem.Add(x);
                }
            }
        }

        public static bool HasCustomReservation(Player player)
        {
            return CustomRoleReservation.Reservations.Any(x => x.UserId == player.UserId);
        }
    }

    public static class Items
    {
        public class BroadcastCard : Item, IPurchasable
        {
            public BroadcastCard(int cost) : base("Broadcast", "全服公告卡", "拥有时，可直接打开控制台(~)输入.bc <内容>\n若有彩名，优先使用彩名公告", true)
            {
                Cost = cost;
            }

            public int Cost { get; }
        }

        public class ScpTrialCard : Item, IUsable, IPurchasable
        {
            public ScpTrialCard(string id, string name, string description, int cost, RoleTypeId role) : base(id, name, description, true)
            {
                RoleType = role;
                Cost = cost;
            }

            public RoleTypeId RoleType { get; }

            public int Cost { get; }

            public bool OnUse(Player player)
            {
                if (!Round.IsLobby)
                {
                    player.SendFormattedMsg("trial_failed", "<color=#4876FF>角色分配</color>", "只可在大厅时使用", 2,
    MsgType.Notification, ScreenLocation.CenterTop, true);
                    return false;
                }
                if (!GameMode.CurrentGameMode.Attribute.RoleReservationEnabled)
                {
                    player.SendFormattedMsg("trial_failed", "<color=#4876FF>角色分配</color>", "当前游戏模式禁用了角色预选", 2,
MsgType.Notification, ScreenLocation.CenterTop, true);
                    return false;
                }
                if (RoleReservation.Reservations.Any(x => x.UserId == player.UserId))
                {
                    player.SendFormattedMsg("trial_failed", "<color=#4876FF>角色分配</color>", "你已经拥有一个预定角色", 2,
                        MsgType.Notification, ScreenLocation.CenterTop, true);
                    return false;
                }
                if (ModuleManager.IsLoaded("CustomCharacter"))
                {
                    if (InventoryHandler.HasCustomReservation(player))
                    {
                        player.SendFormattedMsg("trial_failed", "<color=#4876FF>角色分配</color>", "你已经拥有一个预定角色", 2,
                            MsgType.Notification, ScreenLocation.CenterTop, true);
                        return false;
                    }
                }
                if (RoleReservation.Reservations.Any(x => x.RoleType == RoleType && RoleType != RoleTypeId.None))
                {
                    player.SendFormattedMsg("trial_failed", "<color=#4876FF>角色分配</color>", "来晚啦！已经有其他人预定此角色了", 2,
                        MsgType.Notification, ScreenLocation.CenterTop, true);
                    return false;
                }
                var res = new RoleReservation(player.UserId, RoleType);
                res.OnFailed += Failed;
                res.OnSuccess += Success;
                RoleReservation.Reservations.Add(res);
                Log.Info("USED");
                player.SendFormattedMsg("trial", "<color=#4876FF>角色分配</color>", "已预定该角色，当成功分配后扣除相应体验卡！", 2,
    MsgType.Notification, ScreenLocation.CenterTop, true);
                return false;
            }

            private void Failed(string userid)
            {
                Player.Get(userid)?.SendFormattedMsg("trial", "<color=#4876FF>角色分配</color>", "分配失败，可能是由于生成列表中没有该角色", 2,
    MsgType.Notification, ScreenLocation.CenterTop, true);
            }

            private void Success(string userid)
            {
                InventoryHandler.AllStuffs[userid][this]--;
                InventoryHandler.GameplayData_OnUploadingData(Player.Get(userid));
                Player.Get(userid)?.SendFormattedMsg("trial", "<color=#4876FF>角色分配</color>", "分配成功，已扣除体验卡", 2,
MsgType.Notification, ScreenLocation.CenterTop, true);
            }
        }

        public class CustomRoleTrialCard : Item, IUsable, IPurchasable
        {
            public CustomRoleTrialCard(string id, string name, string description, int cost, Type roletype) : base(id, name, description, true)
            {
                CustomRoleType = roletype;
                Cost = cost;
            }

            public Type CustomRoleType { get; }

            public int Cost { get; }

            public bool OnUse(Player player)
            {
                if (!Round.IsLobby)
                {
                    player.SendFormattedMsg("trial_failed", "<color=#4876FF>角色分配</color>", "只可在大厅时使用", 2,
    MsgType.Notification, ScreenLocation.CenterTop, true);
                    return false;
                }
                if (!GameMode.CurrentGameMode.Attribute.RoleReservationEnabled)
                {
                    player.SendFormattedMsg("trial_failed", "<color=#4876FF>角色分配</color>", "当前游戏模式禁用了角色预选", 2,
MsgType.Notification, ScreenLocation.CenterTop, true);
                    return false;
                }
                if (RoleReservation.Reservations.Any(x => x.UserId == player.UserId))
                {
                    player.SendFormattedMsg("trial_failed", "<color=#4876FF>角色分配</color>", "你已经拥有一个预定角色", 2,
                        MsgType.Notification, ScreenLocation.CenterTop, true);
                    return false;
                }
                if (ModuleManager.IsLoaded("CustomCharacter"))
                {
                    if (InventoryHandler.HasCustomReservation(player))
                    {
                        player.SendFormattedMsg("trial_failed", "<color=#4876FF>角色分配</color>", "你已经拥有一个预定角色", 2,
                            MsgType.Notification, ScreenLocation.CenterTop, true);
                        return false;
                    }
                    if (CustomRoleReservation.Reservations.Any(x => x.RoleType == CustomRoleType))
                    {
                        player.SendFormattedMsg("trial_failed", "<color=#4876FF>角色分配</color>", "来晚啦！已经有其他人预定此角色了", 2,
                            MsgType.Notification, ScreenLocation.CenterTop, true);
                        return false;
                    }
                }
                var res = new CustomRoleReservation(player.UserId, CustomRoleType);
                res.OnFailed += Failed;
                res.OnSuccess += Success;
                CustomRoleReservation.Reservations.Add(res);
                player.SendFormattedMsg("trial", "<color=#4876FF>角色分配</color>", "已预定该角色，当成功分配后扣除相应体验卡！", 2,
                    MsgType.Notification, ScreenLocation.CenterTop, true);
                return false;
            }

            private void Failed(string userid)
            {
                Player.Get(userid)?.SendFormattedMsg("trial", "<color=#4876FF>角色分配</color>", "分配失败，可能是由于生成列表中没有该角色", 2,
MsgType.Notification, ScreenLocation.CenterTop, true);
            }

            private void Success(string userid)
            {
                InventoryHandler.AllStuffs[userid][this]--;
                InventoryHandler.GameplayData_OnUploadingData(Player.Get(userid));
                Player.Get(userid)?.SendFormattedMsg("trial", "<color=#4876FF>角色分配</color>", "分配成功，已扣除体验卡", 2,
MsgType.Notification, ScreenLocation.CenterTop, true);
            }
        }

        public class UnknownItem : Item
        {
            public UnknownItem(string id) : base(id, id, "未知物品，当前服务器上未注册此物品，它可能被删除")
            {
            }
        }

        public class ObstacleItem : Item, ISellable
        {
            public ObstacleItem(string id, string name, int sellcost) : base(id, name, "该物品已注销且不再可用，你可以原价卖掉！")
            {
                Sell_Cost = sellcost;
            }

            public int Sell_Cost { get; }
        }
    }
}